package com.liaoyifan.core.thread;

import com.liaoyifan.core.constant.Constants;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import lombok.Getter;

@SuppressWarnings("unused")
@Getter
public class PoolConfig {

    private static final Map<String, PoolConfig> POOL_CONFIG_CACHE = new ConcurrentHashMap<>();

    private static final int CORE_COUNT = Math.max(5, 2 * Constants.CPU_COUNT + 1);

    private static final int MAX_COUNT = Math.max(10, CORE_COUNT * 2);

    private String poolName;
    private int corePoolSize;
    private int maximumPoolSize;
    private long keepAliveTime;
    private TimeUnit unit;
    private BlockingQueue<Runnable> workQueue;
    private RejectedExecutionHandler handler;
    private long monitorPeriod;
    private boolean shutdown;

    private PoolConfig() {}

    public static PoolConfig create(String poolName) {
        PoolConfig pool = new PoolConfig();
        // 默认配置
        pool.poolName = poolName;
        // 核心线程数
        pool.corePoolSize = CORE_COUNT;
        // 最大线程，队列阻塞时候，扩张线程
        pool.maximumPoolSize = MAX_COUNT;
        // 空闲线程存活时间
        pool.keepAliveTime = 60;
        // 空闲线程存活单位
        pool.unit = TimeUnit.SECONDS;
        // 阻塞队列
        pool.workQueue = new LinkedBlockingQueue<>(1000);
        // 监控间隔时长(300秒)
        pool.monitorPeriod = 300;
        // 使用后是否关闭
        pool.shutdown = false;
        // CallerRunsPolicy	调用者线程直接执行任务，减缓提交速度
        // AbortPolicy	直接抛出 RejectedExecutionException，任务被丢弃
        // DiscardPolicy	静默丢弃新任务，不抛出异常
        // DiscardOldestPolicy	丢弃队列中最老的任务，尝试重新提交当前任务
        pool.handler = new ThreadPoolExecutor.CallerRunsPolicy();
        return pool;
    }

    public static PoolConfig get() {
        return POOL_CONFIG_CACHE.computeIfAbsent(
                Constants.DEFAULT, key -> PoolConfig.create(Constants.DEFAULT));
    }

    public static PoolConfig get(String poolName) {
        return POOL_CONFIG_CACHE.computeIfAbsent(poolName, key -> PoolConfig.create(poolName));
    }

    public static PoolConfig get(String poolName, Supplier<PoolConfig> supplier) {
        return POOL_CONFIG_CACHE.computeIfAbsent(poolName, key -> supplier.get());
    }

    public PoolConfig poolName(String poolName) {
        if (poolName == null || poolName.isBlank()) {
            return this;
        }
        this.poolName = poolName;
        return this;
    }

    public PoolConfig corePoolSize(int corePoolSize) {
        this.corePoolSize = Math.min(corePoolSize, MAX_COUNT);
        return this;
    }

    public PoolConfig keepAliveTime(long keepAliveTime, TimeUnit unit) {
        this.keepAliveTime = keepAliveTime;
        this.unit = unit;
        return this;
    }

    public PoolConfig workQueue(BlockingQueue<Runnable> workQueue) {
        this.workQueue = workQueue;
        return this;
    }

    public PoolConfig rejectedExecutionHandler(RejectedExecutionHandler handler) {
        this.handler = handler;
        return this;
    }

    public PoolConfig monitorPeriod(long monitorPeriod) {
        this.monitorPeriod = monitorPeriod;
        return this;
    }

    public PoolConfig shutdown(boolean shutdown) {
        this.shutdown = shutdown;
        return this;
    }

    public static void remove(String poolName) {
        POOL_CONFIG_CACHE.remove(poolName);
    }

    public static void clear() {
        POOL_CONFIG_CACHE.clear();
    }
}
